// Copyright (c) 2014 Morten Bakkedal
// This code is published under the MIT License.

using System;

using FuncLib.Functions;

namespace FuncLib.DualFunctions.Collections
{
	[Serializable]
	public class HessianEntry : IEquatable<HessianEntry> // FIXME remove, just use Tuple ???
	{
		private Variable variable1, variable2;
		private int hash;

		public HessianEntry(Variable variable1, Variable variable2)
		{
			if (variable1 == null || variable2 == null)
			{
				throw new ArgumentNullException();
			}

			this.variable1 = variable1;
			this.variable2 = variable2;

			// Precompute the hash code since that's almost the purpose of this class.
			int hash1 = variable1.GetHashCode();
			int hash2 = variable2.GetHashCode();

			if (hash1 > hash2)
			{
				// Make sure that equal entries (as defined in Equals) have the same hash code.
				int tmp = hash1;
				hash1 = hash2;
				hash2 = tmp;
			}

			// http://stackoverflow.com/questions/892618/create-a-hashcode-of-two-numbers
			hash = 23;
			unchecked
			{
				hash = hash * 31 + hash1;
				hash = hash * 31 + hash2;
			}
		}

		public override int GetHashCode()
		{
			return hash;
		}

		public bool Equals(HessianEntry other)
		{
			// Follows Item 6 in Effective C# in first three tests.

			if (object.ReferenceEquals(other, null))
			{
				return false;
			}

			if (object.ReferenceEquals(this, other))
			{
				return true;
			}

			if (GetType() != other.GetType())
			{
				return false;
			}

			// Perform unordered comparison.
			return variable1 == other.variable1 && variable2 == other.variable2 || variable1 == other.variable2 && variable2 == other.variable1;
		}

		public override bool Equals(object other)
		{
			return Equals(other as HessianEntry);
		}

		public Variable Variable1
		{
			get
			{
				return variable1;
			}
		}

		public Variable Variable2
		{
			get
			{
				return variable2;
			}
		}

		public static bool operator ==(HessianEntry entry1, HessianEntry entry2)
		{
			if (object.ReferenceEquals(entry1, entry2))
			{
				return true;
			}

			if (object.ReferenceEquals(entry1, null))
			{
				return false;
			}

			return entry1.Equals(entry2);
		}

		public static bool operator !=(HessianEntry entry1, HessianEntry entry2)
		{
			return !(entry1 == entry2);
		}
	}
}
